# Python的垃圾回收机制
"""
Python的垃圾回收机制主要是以引用计数为主，标记清除和分代回收为辅。
"""

# 一、引用计数
"""
引用计数是一种垃圾收集机制，而且也是一种最直观，最简单的垃圾收集技术。
引用计数法Reference Counting的原理是，每个对象都维护一个引用计数字段，记录这个对象被引用的次数
如果有新的引用指向对象，对象引用计数就加一，引用被销毁时，对象引用计数减一，当用户的引用计数为0时，该内存被释放。

每个对象维护着一个ob_ref变量，用来统计当前对象被引用的次数，也就是用来追踪到底有多少个引用指向了这个对象。
当发生以下4种情况时，对象的引用计数加1。

对象被创建，如 a = 1
对象被引用，如 b = a
对象被当做参数传入一个函数中，如func(a)
对象被当做元素存储在容器中时，如li = [a, 2, 3]
与上述情况相对应，当出现以下4种情况时，对象的引用计数减1。

对象被显示销毁，del a
当该对象的引用别名被赋予了新的对象，如 b = 2
当对象离开它的作用域，如函数执行完毕。
当对象从容器中移除，或者容器被销毁时。

引用计数的优点：
    1.高效性
    2.实时性
    3.易于实现
    
引用计数的缺点：
    1.维护引用计数需要消耗额外的资源
    2.无法解决循环引用的问题。例如：
        list1 = []
        list2 = []
        list1.append(list2)
        list2.append(list1)
"""


# 为了弥补引用计数GC机制的上述两个缺点，Python还引入了以下两种GC机制。

# 二、标记清除
"""
标记清除（Mark—Sweep）算法是一种基于追踪回收（tracing GC）技术实现的垃圾回收算法。它分为两个阶段：
第一阶段是标记阶段，GC会把所有的『活动对象』打上标记，
第二阶段是把那些没有标记的对象『非活动对象』进行回收。

那么GC又是如何判断哪些是活动对象哪些是非活动对象的呢？
对象之间通过引用（指针）连在一起，构成一个有向图，对象构成这个有向图的节点，而引用关系构成这个有向图的边。
从根对象（root object）出发，沿着有向边遍历对象，可达的（reachable）对象标记为活动对象，不可达的对象就是要被清除的非活动对象。
根对象就是全局变量、调用栈、寄存器。

标记清除算法作为Python的辅助垃圾收集技术，主要处理的是一些容器对象，比如list、dict、tuple等，
因为对于字符串、数值对象是不可能造成循环引用问题。
Python使用一个双向链表将这些容器对象组织起来。不过，这种简单粗暴的标记清除算法也有明显的缺点：
    清除非活动的对象前它必须顺序扫描整个堆内存，哪怕只剩下小部分活动对象也要扫描所有对象。
"""

# 三、分代回收
"""
gc逻辑

分配内存
    -> 发现超过阈值了
    -> 触发垃圾回收
    -> 将所有可收集对象链表放到一起
    -> 遍历, 计算有效引用计数
    -> 分成 有效引用计数=0 和 有效引用计数 > 0 两个集合
    -> 大于0的, 放入到更老一代
    -> =0的, 执行回收
    -> 回收遍历容器内的各个元素, 减掉对应元素引用计数(破掉循环引用)
    -> 执行-1的逻辑, 若发现对象引用计数=0, 触发内存回收
    -> python底层内存管理机制回收内存

分代回收是一种以空间换时间的操作方式，Python将内存根据对象的存活时间划分为不同的集合，
每个集合称为一个代，Python将内存分为了3“代”，分别为年轻代（第0代）、中年代（第1代）、老年代（第2代），
他们对应的是3个链表，它们的垃圾收集频率与对象的存活时间的增大而减小。新创建的对象都会分配在年轻代，
年轻代链表的总数达到上限时，Python垃圾收集机制就会被触发，把那些可以被回收的对象回收掉，而那些不会回收的对象就会被移到中年代去，
依此类推，老年代中的对象是存活时间最久的对象，甚至是存活于整个系统的生命周期内。同时，分代回收是建立在标记清除技术基础之上。
分代回收同样作为Python的辅助垃圾收集技术处理那些容器对象.
"""